//! Built-in validators
//! 
//! This module implements common validation types used in API testing.

use super::{Validator, ValidationResult, ValidationContext};
use crate::error::Result;
use crate::models::Value;
use serde_json::json;
use regex::Regex;

/// Status code validator
#[derive(Debug)]
pub struct StatusCodeValidator {
    expected: u16,
}

impl StatusCodeValidator {
    pub fn new(expected: u16) -> Self {
        Self { expected }
    }
}

impl Validator for StatusCodeValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value("status_code")
            .and_then(|v| v.as_u64())
            .map(|v| v as u16);
        
        match actual {
            Some(status) if status == self.expected => {
                Ok(ValidationResult::success(
                    "status_code",
                    &format!("Status code is {}", self.expected),
                ))
            }
            Some(status) => {
                Ok(ValidationResult::failure(
                    "status_code",
                    &format!("Expected status code {}, got {}", self.expected, status),
                    Some(json!(self.expected)),
                    Some(json!(status)),
                ))
            }
            None => {
                Ok(ValidationResult::failure(
                    "status_code",
                    "Status code not found in response",
                    Some(json!(self.expected)),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "status_code"
    }
}

/// Equals validator for exact value matching
#[derive(Debug)]
pub struct EqualsValidator {
    path: String,
    expected: Value,
}

impl EqualsValidator {
    pub fn new(path: String, expected: Value) -> Self {
        Self { path, expected }
    }
}

impl Validator for EqualsValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value(&self.path);
        
        match actual {
            Some(value) if value == &self.expected => {
                Ok(ValidationResult::success(
                    "equals",
                    &format!("Value at '{}' equals expected", self.path),
                ))
            }
            Some(value) => {
                Ok(ValidationResult::failure(
                    "equals",
                    &format!("Value at '{}' does not match expected", self.path),
                    Some(self.expected.clone()),
                    Some(value.clone()),
                ))
            }
            None => {
                Ok(ValidationResult::failure(
                    "equals",
                    &format!("Path '{}' not found in response", self.path),
                    Some(self.expected.clone()),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "equals"
    }
}

/// Contains validator for substring/element checking
#[derive(Debug)]
pub struct ContainsValidator {
    path: String,
    expected: Value,
}

impl ContainsValidator {
    pub fn new(path: String, expected: Value) -> Self {
        Self { path, expected }
    }
}

impl Validator for ContainsValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value(&self.path);
        
        match actual {
            Some(value) => {
                let contains = match (value, &self.expected) {
                    (Value::String(text), Value::String(substring)) => {
                        text.contains(substring)
                    }
                    (Value::Array(arr), expected_val) => {
                        arr.iter().any(|item| item == expected_val)
                    }
                    (Value::Object(obj), Value::String(key)) => {
                        obj.contains_key(key)
                    }
                    _ => false,
                };
                
                if contains {
                    Ok(ValidationResult::success(
                        "contains",
                        &format!("Value at '{}' contains expected", self.path),
                    ))
                } else {
                    Ok(ValidationResult::failure(
                        "contains",
                        &format!("Value at '{}' does not contain expected", self.path),
                        Some(self.expected.clone()),
                        Some(value.clone()),
                    ))
                }
            }
            None => {
                Ok(ValidationResult::failure(
                    "contains",
                    &format!("Path '{}' not found in response", self.path),
                    Some(self.expected.clone()),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "contains"
    }
}

/// Length validator for arrays and strings
#[derive(Debug)]
pub struct LengthValidator {
    path: String,
    expected_length: usize,
}

impl LengthValidator {
    pub fn new(path: String, expected_length: usize) -> Self {
        Self { path, expected_length }
    }
}

impl Validator for LengthValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value(&self.path);
        
        match actual {
            Some(value) => {
                let actual_length = match value {
                    Value::String(s) => Some(s.len()),
                    Value::Array(arr) => Some(arr.len()),
                    Value::Object(obj) => Some(obj.len()),
                    _ => None,
                };
                
                match actual_length {
                    Some(length) if length == self.expected_length => {
                        Ok(ValidationResult::success(
                            "length",
                            &format!("Length of '{}' is {}", self.path, self.expected_length),
                        ))
                    }
                    Some(length) => {
                        Ok(ValidationResult::failure(
                            "length",
                            &format!("Expected length {}, got {}", self.expected_length, length),
                            Some(json!(self.expected_length)),
                            Some(json!(length)),
                        ))
                    }
                    None => {
                        Ok(ValidationResult::failure(
                            "length",
                            &format!("Cannot determine length of value at '{}'", self.path),
                            Some(json!(self.expected_length)),
                            Some(value.clone()),
                        ))
                    }
                }
            }
            None => {
                Ok(ValidationResult::failure(
                    "length",
                    &format!("Path '{}' not found in response", self.path),
                    Some(json!(self.expected_length)),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "length"
    }
}

/// Regex validator for pattern matching
#[derive(Debug)]
pub struct RegexValidator {
    path: String,
    pattern: Regex,
    pattern_str: String,
}

impl RegexValidator {
    pub fn new(path: String, pattern: &str) -> Result<Self> {
        let regex = Regex::new(pattern)
            .map_err(|e| crate::error::Error::Validation(
                crate::error::ValidationError::InvalidPattern {
                    pattern: pattern.to_string(),
                    message: e.to_string(),
                }
            ))?;
        
        Ok(Self {
            path,
            pattern: regex,
            pattern_str: pattern.to_string(),
        })
    }
}

impl Validator for RegexValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value(&self.path);
        
        match actual {
            Some(Value::String(text)) => {
                if self.pattern.is_match(text) {
                    Ok(ValidationResult::success(
                        "regex",
                        &format!("Value at '{}' matches pattern", self.path),
                    ))
                } else {
                    Ok(ValidationResult::failure(
                        "regex",
                        &format!("Value at '{}' does not match pattern", self.path),
                        Some(json!(self.pattern_str)),
                        Some(json!(text)),
                    ))
                }
            }
            Some(value) => {
                Ok(ValidationResult::failure(
                    "regex",
                    &format!("Value at '{}' is not a string", self.path),
                    Some(json!(self.pattern_str)),
                    Some(value.clone()),
                ))
            }
            None => {
                Ok(ValidationResult::failure(
                    "regex",
                    &format!("Path '{}' not found in response", self.path),
                    Some(json!(self.pattern_str)),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "regex"
    }
}

/// Type validator for checking value types
#[derive(Debug)]
pub struct TypeValidator {
    path: String,
    expected_type: String,
}

impl TypeValidator {
    pub fn new(path: String, expected_type: String) -> Self {
        Self { path, expected_type }
    }
}

impl Validator for TypeValidator {
    fn validate(&self, context: &ValidationContext) -> Result<ValidationResult> {
        let actual = context.get_response_value(&self.path);
        
        match actual {
            Some(value) => {
                let actual_type = match value {
                    Value::Null => "null",
                    Value::Bool(_) => "boolean",
                    Value::Number(n) if n.is_i64() => "integer",
                    Value::Number(_) => "number",
                    Value::String(_) => "string",
                    Value::Array(_) => "array",
                    Value::Object(_) => "object",
                };
                
                if actual_type == self.expected_type {
                    Ok(ValidationResult::success(
                        "type",
                        &format!("Value at '{}' is of type {}", self.path, self.expected_type),
                    ))
                } else {
                    Ok(ValidationResult::failure(
                        "type",
                        &format!("Expected type {}, got {}", self.expected_type, actual_type),
                        Some(json!(self.expected_type)),
                        Some(json!(actual_type)),
                    ))
                }
            }
            None => {
                Ok(ValidationResult::failure(
                    "type",
                    &format!("Path '{}' not found in response", self.path),
                    Some(json!(self.expected_type)),
                    None,
                ))
            }
        }
    }
    
    fn name(&self) -> &str {
        "type"
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;
    use serde_json::json;

    fn create_test_context() -> ValidationContext {
        let response = HashMap::from([
            ("status_code".to_string(), json!(200)),
            ("body".to_string(), json!({
                "message": "Hello, World!",
                "data": {
                    "items": [1, 2, 3],
                    "count": 3,
                    "active": true
                }
            })),
        ]);
        
        ValidationContext::new(response, HashMap::new())
    }

    #[test]
    fn test_status_code_validator() {
        let context = create_test_context();
        
        // Test successful validation
        let validator = StatusCodeValidator::new(200);
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test failed validation
        let validator = StatusCodeValidator::new(404);
        let result = validator.validate(&context).unwrap();
        assert!(!result.success);
    }

    #[test]
    fn test_equals_validator() {
        let context = create_test_context();
        
        // Test successful validation
        let validator = EqualsValidator::new("body.message".to_string(), json!("Hello, World!"));
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test failed validation
        let validator = EqualsValidator::new("body.message".to_string(), json!("Goodbye"));
        let result = validator.validate(&context).unwrap();
        assert!(!result.success);
    }

    #[test]
    fn test_contains_validator() {
        let context = create_test_context();
        
        // Test string contains
        let validator = ContainsValidator::new("body.message".to_string(), json!("Hello"));
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test array contains
        let validator = ContainsValidator::new("body.data.items".to_string(), json!(2));
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
    }

    #[test]
    fn test_length_validator() {
        let context = create_test_context();
        
        // Test array length
        let validator = LengthValidator::new("body.data.items".to_string(), 3);
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test string length
        let validator = LengthValidator::new("body.message".to_string(), 13);
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
    }

    #[test]
    fn test_regex_validator() {
        let context = create_test_context();
        
        // Test successful regex match
        let validator = RegexValidator::new("body.message".to_string(), r"Hello.*").unwrap();
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test failed regex match
        let validator = RegexValidator::new("body.message".to_string(), r"Goodbye.*").unwrap();
        let result = validator.validate(&context).unwrap();
        assert!(!result.success);
    }

    #[test]
    fn test_type_validator() {
        let context = create_test_context();
        
        // Test correct type
        let validator = TypeValidator::new("body.data.count".to_string(), "integer".to_string());
        let result = validator.validate(&context).unwrap();
        assert!(result.success);
        
        // Test incorrect type
        let validator = TypeValidator::new("body.data.count".to_string(), "string".to_string());
        let result = validator.validate(&context).unwrap();
        assert!(!result.success);
    }
}
